import sys
import json
import socket
import os
import asyncio
import http.server
import socketserver
from threading import Thread
from PySide6.QtWidgets import (
    QApplication,
    QMainWindow,
    QWidget,
    QVBoxLayout,
    QHBoxLayout,
    QLabel,
    QLineEdit,
    QPushButton,
    QTextEdit,
    QGridLayout,
    QScrollArea,
    QFrame,
    QSizePolicy,
    QStatusBar,
    QFileDialog,
    QGroupBox,
    QProgressBar,
    QMessageBox,
    QCheckBox,
    QComboBox,
)
from PySide6.QtCore import QThread, Signal, Qt, QSettings
from PySide6.QtGui import QFont
import websockets
import logging

# Constants
DEFAULT_WEBSOCKET_PORT = 8765
DEFAULT_HTTP_PORT = 55555
CONFIG_FILE = "config.ini"
DEFAULT_OTA_FILENAME = "nb_wifi_v3400.bin"
DEFAULT_OTA_FILEPATH = os.path.join(os.getcwd(), DEFAULT_OTA_FILENAME)
WEBSOCKET_URL_PREFIX = "ws://"
HTTP_URL_PREFIX = "http://"
LOG_FILE = "v3400_monitor.log"


# Set up root logger
logger = logging.getLogger()
logger.setLevel(logging.NOTSET)  # Start with logging disabled
log_handler = logging.FileHandler(LOG_FILE)
log_formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
log_handler.setFormatter(log_formatter)

# create a logger, this will make sure there is only one handler
if logger.hasHandlers():
    logger.handlers.clear()
logger.addHandler(log_handler)


class WebSocketServerThread(QThread):
    # 自定义信号，传递接收到的数据
    data_received = Signal(str, str)  # Modified Signal to include client address
    server_stopped = Signal()  # 添加一个信号，当服务器停止时发出
    client_connected = Signal(str)  # 添加一个信号，当有客户端连接时发出
    client_disconnected = Signal(str)  # 添加一个信号，当有客户端断开时发出
    client_list_updated = Signal(list)  # 添加一个信号，当客户端列表发生变化时发出

    def __init__(self, host, port, logging_enabled):
        super().__init__()
        self.host = host
        self.port = port
        self.server_task = None
        self.loop = asyncio.new_event_loop()
        self.running = False
        self.connected_clients = {}  # 保存客户端连接信息的字典 {websocket: address}
        self.logging_enabled = logging_enabled
        logger.debug(f"WebSocket Server logging initialized, status:{logging_enabled}")

    def log_if_enabled(self, level, message):
        if self.logging_enabled:
            logger.log(level, message)

    async def handler(self, websocket):
        try:
            address = websocket.remote_address
            client_address = f"{address[0]}:{address[1]}"
            self.connected_clients[websocket] = client_address
            self.client_connected.emit(client_address)
            self.client_list_updated.emit(
                list(self.connected_clients.values())
            )  # 发送客户端列表
            async for message in websocket:
                self.data_received.emit(
                    message, client_address
                )  # Pass client address with data
        except websockets.exceptions.ConnectionClosed as e:
            self.log_if_enabled(
                logging.INFO, f"Client disconnected: {client_address} - {e}"
            )

        except Exception as e:
            self.log_if_enabled(logging.ERROR, f"Error in handler: {e}")
        finally:
            if websocket in self.connected_clients:
                client_address = self.connected_clients[websocket]
                del self.connected_clients[websocket]
                self.client_disconnected.emit(client_address)
                self.client_list_updated.emit(
                    list(self.connected_clients.values())
                )  # 发送客户端列表

    def send_message(self, message):
        if self.server_task and self.running:
            asyncio.run_coroutine_threadsafe(self._send_message(message), self.loop)

    async def _send_message(self, message):
        if self.connected_clients:
            websockets_list = list(self.connected_clients.keys())
            for websocket in websockets_list:
                try:
                    await websocket.send(message)
                except Exception as e:
                    self.log_if_enabled(logging.ERROR, f"Error sending message: {e}")

    async def server(self):
        try:
            self.server_task = await websockets.serve(
                self.handler, self.host, self.port, reuse_port=False
            )
            self.running = True
            self.log_if_enabled(
                logging.INFO, f"Server started on ws://{self.host}:{self.port}"
            )
            await self.server_task.wait_closed()

        except Exception as e:
            self.log_if_enabled(logging.ERROR, f"Error starting server: {e}")
            self.server_task = None
        finally:
            if self.server_task and not self.server_task.is_serving():
                self.log_if_enabled(logging.INFO, "Server stopped")
                self.server_stopped.emit()

    def run(self):
        asyncio.set_event_loop(self.loop)
        try:
            self.loop.run_until_complete(self.server())
        except Exception as e:
            self.log_if_enabled(logging.ERROR, f"Server error in run: {e}")
        finally:
            if self.loop.is_running():
                self.loop.close()

    def stop(self):
        if self.server_task:
            try:
                self.loop.call_soon_threadsafe(self.close_server)  # 改成异步方式

            except Exception as e:
                self.log_if_enabled(logging.ERROR, f"Error stopping server: {e}")
            finally:
                self.running = False

    def close_server(self):
        async def stop_server():
            self.server_task.close()
            await self.server_task.wait_closed()
            self.server_stopped.emit()  # 在close_server方法中发出信号

        self.loop.create_task(stop_server())


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("V3400 Environment Monitor v1.0")
        self.setStyleSheet("background-color: #f5f7fa; color: black;")
        self.settings = QSettings(CONFIG_FILE, QSettings.IniFormat)

        # Main window layout
        self.central_widget = QWidget()
        self.setCentralWidget(self.central_widget)
        self.main_layout = QVBoxLayout(self.central_widget)

        # Group boxes for server, config, and data display
        self.server_group = QGroupBox("Server Control")
        self.config_group = QGroupBox("Configuration")
        self.data_group = QGroupBox("Data Display")

        self.main_layout.addWidget(self.server_group)
        self.main_layout.addWidget(self.config_group)
        self.main_layout.addWidget(self.data_group)

        self.server_group_layout = QVBoxLayout()
        self.config_group_layout = QVBoxLayout()
        self.server_group.setLayout(self.server_group_layout)
        self.config_group.setLayout(self.config_group_layout)

        # Server IP & Port Layout
        server_ip_port_layout = QHBoxLayout()
        server_ip_port_layout.setAlignment(Qt.AlignLeft)
        self.server_group_layout.addLayout(server_ip_port_layout)

        self.ip_label = QLabel("Server IP:")
        self.ip_label.setFont(QFont("Arial", 10, QFont.Bold))
        self.ip_label.setFixedWidth(60)
        server_ip_port_layout.addWidget(self.ip_label)

        self.ip_input = QLineEdit(self.get_local_ip())
        self.ip_input.setFixedWidth(150)
        self.ip_input.setAlignment(Qt.AlignLeft)
        self.ip_input.setStyleSheet(self.get_lineedit_stylesheet())
        server_ip_port_layout.addWidget(self.ip_input)

        self.port_label = QLabel("Port:")
        self.port_label.setFont(QFont("Arial", 10, QFont.Bold))
        self.port_label.setFixedWidth(40)
        server_ip_port_layout.addWidget(self.port_label)

        self.port_input = QLineEdit(str(DEFAULT_WEBSOCKET_PORT))
        self.port_input.setFixedWidth(100)
        self.port_input.setAlignment(Qt.AlignLeft)
        self.port_input.setStyleSheet(self.get_lineedit_stylesheet())
        server_ip_port_layout.addWidget(self.port_input)

        self.start_button = QPushButton("Start Server")
        self.start_button.setFixedWidth(150)
        self.start_button.setStyleSheet(
            "background-color: #e81123; color: white; padding: 8px 12px; border-radius: 5px;font: bold;"
        )
        self.start_button.clicked.connect(self.start_server)
        server_ip_port_layout.addWidget(self.start_button)

        self.clear_button = QPushButton("Clear")
        self.clear_button.setFixedWidth(150)
        self.clear_button.setStyleSheet(self.get_pushbutton_stylesheet())
        self.clear_button.clicked.connect(self.clear_display)
        server_ip_port_layout.addWidget(self.clear_button)

        # WS URL
        ws_url_layout = QHBoxLayout()
        ws_url_layout.setAlignment(Qt.AlignLeft)
        self.config_group_layout.addLayout(ws_url_layout)

        self.ws_url_label = QLabel("WS  Server:")
        self.ws_url_label.setFont(QFont("Arial", 10, QFont.Bold))
        self.ws_url_label.setFixedWidth(80)
        ws_url_layout.addWidget(self.ws_url_label)

        local_ip = self.get_local_ip()
        self.ws_url_input = QLineEdit(
            f"{WEBSOCKET_URL_PREFIX}{local_ip}:{DEFAULT_WEBSOCKET_PORT}"
        )
        self.ws_url_input.setFixedWidth(400)
        self.ws_url_input.setAlignment(Qt.AlignLeft)
        self.ws_url_input.setStyleSheet(self.get_lineedit_stylesheet())
        ws_url_layout.addWidget(self.ws_url_input)

        self.set_url_button = QPushButton("Set")
        self.set_url_button.setStyleSheet(self.get_pushbutton_stylesheet())
        self.set_url_button.setFixedWidth(150)
        self.set_url_button.clicked.connect(self.send_ws_url_to_device)
        ws_url_layout.addWidget(self.set_url_button)

        # OTA URL & file
        ota_url_layout = QHBoxLayout()
        ota_url_layout.setAlignment(Qt.AlignLeft)
        self.config_group_layout.addLayout(ota_url_layout)

        self.ota_url_label = QLabel("OTA Server:")
        self.ota_url_label.setFont(QFont("Arial", 10, QFont.Bold))
        self.ota_url_label.setFixedWidth(80)
        ota_url_layout.addWidget(self.ota_url_label)

        self.ota_url_input = QLineEdit(
            f"{HTTP_URL_PREFIX}{local_ip}:{DEFAULT_HTTP_PORT}/{DEFAULT_OTA_FILENAME}"
        )
        self.ota_url_input.setFixedWidth(400)
        self.ota_url_input.setAlignment(Qt.AlignLeft)
        self.ota_url_input.setStyleSheet(self.get_lineedit_stylesheet())
        ota_url_layout.addWidget(self.ota_url_input)

        self.set_ota_url_button = QPushButton("Start OTA")
        self.set_ota_url_button.setStyleSheet(self.get_pushbutton_stylesheet())
        self.set_ota_url_button.setFixedWidth(150)
        self.set_ota_url_button.clicked.connect(self.start_ota_update)
        ota_url_layout.addWidget(self.set_ota_url_button)

        # 数据展示区
        self.data_layout = QHBoxLayout()
        self.data_layout.setContentsMargins(10, 10, 10, 10)
        self.data_group.setLayout(self.data_layout)

        # 左侧：JSON 数据展示区
        self.data_display = QTextEdit()
        self.data_display.setReadOnly(True)
        self.data_display.setStyleSheet(
            "background-color: white; border: 1px solid #ccc; border-radius: 5px; padding: 5px;"
        )
        self.data_layout.addWidget(self.data_display, 1)

        # 中间： 其它消息展示
        container = QWidget()
        container.setFixedWidth(250)

        self.other_layout = QVBoxLayout(container)
        self.other_layout.setContentsMargins(5, 5, 5, 5)

        # Logging Checkbox
        self.logging_checkbox = QCheckBox("Enable Logging")
        self.logging_checkbox.setChecked(False)
        self.logging_checkbox.stateChanged.connect(self.toggle_logging)
        self.other_layout.addWidget(self.logging_checkbox)

        # Client Selection Dropdown
        self.client_dropdown = QComboBox()
        self.client_dropdown.currentIndexChanged.connect(self.select_client)
        self.client_dropdown.setStyleSheet(self.get_combobox_stylesheet())
        self.other_layout.addWidget(self.client_dropdown)

        # OTA Progress Bar
        self.ota_progress_bar = QProgressBar()
        self.ota_progress_bar.setMaximum(100)
        self.ota_progress_bar.setValue(0)
        self.ota_progress_bar.setVisible(True)  # Initially hide the progress bar
        self.ota_progress_bar.setStyleSheet(self.get_progressbar_stylesheet())
        self.other_layout.addWidget(self.ota_progress_bar)

        self.other_layout.addStretch()
        self.data_layout.addWidget(container)

        # 右侧：advertValue 数据展示区
        self.advert_scroll_area = QScrollArea()
        self.advert_widget = QWidget()
        self.advert_layout = QGridLayout(self.advert_widget)
        self.advert_scroll_area.setWidgetResizable(True)
        self.advert_scroll_area.setWidget(self.advert_widget)
        self.advert_scroll_area.setStyleSheet(
            "background-color: white; border: 1px solid #ccc; border-radius: 5px; padding: 5px;"
        )
        self.data_layout.addWidget(self.advert_scroll_area, 2)

        self.server_thread = None
        self.advert_labels = {}
        self.unit_labels = {}
        self.ota_file_path = DEFAULT_OTA_FILEPATH  # store the OTA file path here
        self.selected_client = None  # keep track of the selected client

        # Initialize advert fields
        self.init_advert_fields()

        # Add status bar
        self.status_bar = QStatusBar()
        self.setStatusBar(self.status_bar)
        self.client_info_label = QLabel("No clients connected")
        self.status_bar.addWidget(self.client_info_label)
        self.http_server_thread = None
        self.load_settings()
        self.logging_enabled = False  # Initialize logging state
        self.log_handler = log_handler  # save a copy of the handler to the object

    def get_progressbar_stylesheet(self):
        return """
            QProgressBar {
                border: 2px solid #4CAF50; /* Border color */
                border-radius: 10px;        /* Rounded corners */
                text-align: center;         /* Center text */
                background-color: #f0f0f0; /* Background */
                color: #333; /* Text color */
                font-size: 14px;
                min-height: 25px;
                max-height: 25px;
                margin-left: 10px;
                margin-right: 10px;
            }

            QProgressBar::chunk {
                background-color: qlineargradient(x1:0, y1:0, x2:1, y2:0,
                                                  stop:0 #8BC34A, stop:1 #4CAF50);  /* Gradient progress fill */
                border-radius: 8px;         /* Rounded corners for the chunk */
            }
        """

    def get_combobox_stylesheet(self):
        return """
            QComboBox {
                border: 2px solid #4CAF50; /* Border color */
                border-radius: 5px;        /* Rounded corners */
                padding: 5px;             /* Padding within the box */
                background-color: #ffffff; /* Background color */
                color: #333;              /* Text color */
                font-size: 14px;
                selection-background-color: #e0e0e0;  /* Background of selected item in dropdown */
                selection-color: #333;     /* Text color of selected item in dropdown */
                min-height: 25px;         /* Minimum height of the combo box */
                margin-left: 10px;
                margin-right: 10px;
            }

            QComboBox:hover {
                 border-color: #81C784; /* Hover color */
            }


            QComboBox::drop-down {
                subcontrol-origin: padding;
                subcontrol-position: top right;
                width: 20px;
                border-left: 1px solid #4CAF50; /* Left border of the dropdown arrow */
                border-top-right-radius: 3px; /* Rounded top-right corner of arrow */
                border-bottom-right-radius: 3px; /* Rounded bottom-right corner of arrow */
            }
             QComboBox::drop-down:hover {
                border-left-color: #81C784;
            }

           QComboBox::down-arrow {
                image: url("arrow_down.png"); /*Replace with your arrow image path */
                width: 10px;
                height: 10px;
            }

            QComboBox QAbstractItemView {
                border: 1px solid #4CAF50; /* Border for the dropdown list */
                selection-background-color: #e0e0e0; /* Highlighted selection in dropdown list */
                selection-color: #333; /* Color of the text of the highlighted selection */
                outline: 0; /* Remove any outline */
            }
        """
        # you can also replace the image arrow with a unicode arrow like this:
        """
            QComboBox::down-arrow {
                width: 10px;
                height: 10px;
                image: url("");
            }
        """

    def get_lineedit_stylesheet(self):
        return """
        QLineEdit {
            border: 2px solid #4CAF50;
            border-radius: 5px;
            padding: 5px;
        }
        QLineEdit:hover {
            border-color: #999;
        }
         QLineEdit:focus {
            border-color: #66afe9;
            border-width: 2px;;
        }
        """

    def get_pushbutton_stylesheet(self):
        return """
        QPushButton {
            background-color: #d9eaff;
            color: black;
            padding: 8px 12px;
            border: none;
            border-radius: 5px;
            border-style: outset;
         }
          QPushButton:hover {
            background-color: #b9eaff;
         }
         QPushButton:pressed {
            background-color: #a9eaff;
            border-style: inset;
         }
        """

    def closeEvent(self, event):
        """Override closeEvent to gracefully shut down server threads"""
        logger.info("Closing main window...")
        if self.server_thread and self.server_thread.running:
            self.server_thread.stop()
        if self.http_server_thread and self.http_server_thread.running:
            self.http_server_thread.stop()
        event.accept()

    def load_settings(self):
        self.ip_input.setText(self.settings.value("ip", self.get_local_ip()))
        self.port_input.setText(
            self.settings.value("port", str(DEFAULT_WEBSOCKET_PORT))
        )
        self.ws_url_input.setText(
            self.settings.value(
                "ws_url",
                f"{WEBSOCKET_URL_PREFIX}{self.get_local_ip()}:{DEFAULT_WEBSOCKET_PORT}",
            )
        )
        self.ota_url_input.setText(
            self.settings.value(
                "ota_url",
                f"{HTTP_URL_PREFIX}{self.get_local_ip()}:{DEFAULT_HTTP_PORT}/{DEFAULT_OTA_FILENAME}",
            )
        )
        self.logging_checkbox.setChecked(
            self.settings.value("logging_enabled", False, type=bool)
        )
        self.toggle_logging(self.logging_checkbox.checkState())  # set the logger state

    def save_settings(self):
        self.settings.setValue("ip", self.ip_input.text())
        self.settings.setValue("port", self.port_input.text())
        self.settings.setValue("ws_url", self.ws_url_input.text())
        self.settings.setValue("ota_url", self.ota_url_input.text())
        self.settings.setValue("logging_enabled", self.logging_checkbox.isChecked())

    def init_advert_fields(self):
        fields = [
            ("Device Type", None),
            ("HW Version", None),
            ("SW Version", None),
            ("Ambient Noise", "dB"),
            ("CO2", "ppm"),
            ("PM2.5 (CF1)", "µg/m³"),
            ("PM2.5 (Ambient)", "µg/m³"),
            ("Ambient Light", "Lux"),
            ("UV", "UVI"),
            ("TVOC", "ppb"),
            ("CO2 (VOC)", "ppm"),
            ("Temperature", "°C"),
            ("Humidity", "%RH"),
        ]
        for row, (field, unit) in enumerate(fields):
            label_key = QLabel(field)
            label_key.setFont(QFont("Arial", 10, QFont.Bold))
            label_key.setFixedHeight(30)
            label_key.setAlignment(Qt.AlignLeft)
            label_key.setStyleSheet(
                "padding-left: 10px; background-color: #eef2f5; border-radius: 5px;"
            )

            label_value = QLabel("--")
            label_value.setFont(QFont("Arial", 10))
            label_value.setFixedHeight(30)
            label_value.setAlignment(Qt.AlignCenter)
            label_value.setStyleSheet(
                "background-color: #ffffff; border: 1px solid #ccc; border-radius: 5px;"
            )

            self.advert_layout.addWidget(label_key, row, 0)
            self.advert_layout.addWidget(label_value, row, 1)
            self.advert_labels[field] = label_value

            if unit:
                label_unit = QLabel(unit)
                label_unit.setFont(QFont("Arial", 10))
                label_unit.setFixedHeight(30)
                label_unit.setAlignment(Qt.AlignCenter)
                label_unit.setStyleSheet(
                    "background-color: #eef2f5; border-radius: 5px;"
                )
                self.advert_layout.addWidget(label_unit, row, 2)
                self.unit_labels[field] = label_unit

    def get_local_ip(self):
        try:
            s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            s.connect(("8.8.8.8", 80))
            ip = s.getsockname()[0]
            s.close()
            return ip
        except Exception:
            return "127.0.0.1"

    def start_server(self):
        ip = self.ip_input.text()
        port = self.port_input.text()
        if not port.isdigit():
            QMessageBox.critical(self, "Error", "Port must be a number.")
            return

        port = int(port)

        if self.server_thread is None or not self.server_thread.running:
            self.server_thread = WebSocketServerThread(ip, port, self.logging_enabled)
            self.server_thread.data_received.connect(self.display_data)
            self.server_thread.server_stopped.connect(
                self.on_server_stopped
            )  # 连接 server_stopped 信号
            self.server_thread.client_connected.connect(
                self.on_client_connected
            )  # 连接 client_connected 信号
            self.server_thread.client_disconnected.connect(
                self.on_client_disconnected
            )  # 连接 client_disconnected 信号
            self.server_thread.client_list_updated.connect(
                self.update_client_list_display
            )  # 连接 client_list_updated 信号
            self.server_thread.start()
            self.start_button.setText("Stop Server")
            self.start_button.setStyleSheet(
                "background-color: #28a745; color: white; padding: 8px 12px; border-radius: 5px;font: bold;"
            )
            self.save_settings()

        else:
            self.server_thread.stop()

    def on_server_stopped(self):
        self.server_thread = None
        self.start_button.setText("Start Server")
        self.start_button.setStyleSheet(
            "background-color: #e81123; color: white; padding: 8px 12px; border-radius: 5px;font: bold;"
        )

    def on_client_connected(self, address):
        self.client_info_label.setText(f"Client connected: {address}")

    def on_client_disconnected(self, address):
        self.client_info_label.setText(f"Client disconnected: {address}")

    def update_client_list_display(self, client_list):
        self.client_dropdown.clear()
        self.client_dropdown.addItems(client_list)
        if client_list:
            self.client_info_label.setText(
                f"Client connected: {', '.join(client_list)}"
            )
            if not self.selected_client:
                self.select_client(0)
        else:
            self.client_info_label.setText("No clients connected")
            self.selected_client = None

    def select_client(self, index):
        if index >= 0 and index < self.client_dropdown.count():
            self.selected_client = self.client_dropdown.itemText(index)
        else:
            self.selected_client = None
            logger.warning(f"Selected client index is out of bounds: {index}")
        logger.debug(f"Selected client: {self.selected_client}")

    def display_data(self, message, client_address):
        # if self.selected_client is None or self.selected_client == client_address:
        if True:
            try:
                parsed_message = json.loads(message)
                formatted_message = json.dumps(parsed_message, indent=4)
                self.data_display.append(formatted_message)
                if self.logging_enabled:
                    logger.debug(
                        f"Received data: {formatted_message} from {client_address}"
                    )

                # 解析OTA进度消息
                if (
                    "payload" in parsed_message
                    and "process" in parsed_message["payload"]
                ):
                    progress = parsed_message["payload"]["process"]
                    print(f'progress: {progress}')
                    if isinstance(progress, int):
                        self.ota_progress_bar.setValue(progress)
                    else:
                        if self.logging_enabled:
                            logger.warning(f"Invalid OTA progress value: {progress}")

                # 解析广播数据
                if "payload" in parsed_message:
                    advert_value = parsed_message["payload"]["advertValue"]
                    parsed_advert = self.parse_advert_value(advert_value)
                    self.update_advert_fields(parsed_advert)


            except json.JSONDecodeError as e:
                self.data_display.append(f"Invalid JSON: {message}")
                if self.logging_enabled:
                    logger.error(
                        f"Invalid JSON received: {message} - {e} from {client_address}"
                    )
            except Exception as e:
                self.data_display.append(f"Error in display_data: {e}")
                if self.logging_enabled:
                    logger.error(f"Error in display_data: {e} from {client_address}")
        else:
            logger.debug(
                f"Message Ignored {client_address}, Current Selection: {self.selected_client}"
            )

    def clear_display(self):
        self.data_display.clear()
        for label in self.advert_labels.values():
            label.setText("--")

    def parse_advert_value(self, advert_value):
        try:
            data = bytes.fromhex(advert_value)

            if len(data) != 30:
                return {
                    "Error": f"Invalid advert data length: {len(data)} bytes. Expected 28 bytes."
                }
            parsed = {
                "Device Type": data[2:4].hex(),
                "HW Version": f"{data[4] >> 4}.{data[4] & 0x0F}",
                "SW Version": f"{data[5] >> 4}.{data[5] & 0x0F}.{data[6]}.{data[7]}",
                "Ambient Noise": f"{int.from_bytes(data[8:10], 'little') / 10}",
                "CO2": f"{int.from_bytes(data[10:12], 'little')}",
                "PM2.5 (CF1)": f"{int.from_bytes(data[12:14], 'little')}",
                "PM2.5 (Ambient)": f"{int.from_bytes(data[14:16], 'little')}",
                "Ambient Light": f"{int.from_bytes(data[16:20], 'little') / 100}",
                "UV": f"{int.from_bytes(data[20:22], 'little') / 100}",
                "TVOC": f"{int.from_bytes(data[22:24], 'little')}",
                "CO2 (VOC)": f"{int.from_bytes(data[24:26], 'little')}",
                "Temperature": f"{int.from_bytes(data[26:28], 'little', signed=True) / 100}",
                "Humidity": f"{int.from_bytes(data[28:30], 'little') / 100}",
            }
            return parsed
        except Exception as e:
            if self.logging_enabled:
                logger.error(f"Error parsing advert value: {advert_value} - {e}")
            return {"Error": str(e)}

    def update_advert_fields(self, parsed_advert):
        for key, value in parsed_advert.items():
            if key in self.advert_labels:
                self.advert_labels[key].setText(value)

    def send_ws_url_to_device(self):
        ws_url = self.ws_url_input.text()
        message = {"system":{"ws_server_url": ws_url}}
        json_message = json.dumps(message)
        if self.server_thread:
            self.server_thread.send_message(json_message)
            if self.logging_enabled:
                logger.info(f"Sent WebSocket URL to device: {ws_url}")

    def start_ota_update(self):
        self.ota_progress_bar.setValue(0)
        ota_url = self.ota_url_input.text()
        message = {"system":{"ota_server_url": ota_url}}
        json_message = json.dumps(message)
        if self.server_thread:
            self.server_thread.send_message(json_message)
            if self.logging_enabled:
                logger.info(f"Sent OTA URL to device: {ota_url}")

    def toggle_logging(self, state):
        self.logging_enabled = state == 2
        if self.logging_enabled:
            logger.setLevel(logging.DEBUG)
            logger.addHandler(log_handler)  # Add the handler back
            logger.info("Logging enabled")

        else:
            logger.setLevel(logging.NOTSET)
            logger.removeHandler(log_handler)  # Remove the handler
            logger.info("Logging disabled")


if __name__ == "__main__":
    app = QApplication(sys.argv)

    window = MainWindow()
    window.resize(1000, 750)
    window.show()

    sys.exit(app.exec())
